/*
 renderable.h
 Dynamical
 Matthew Jee
 mcjee@ucsc.edu
 
 Shader Requirements:
 modelMatrix
 inverseModelMatrix
*/

#ifndef MJ_RENDERABLE_H
#define MJ_RENDERABLE_H

#include <vector>
#include "transform.h"
#include "definitions.h"
#include "matrix.h"
#include "shader.h"
#include "mesh.h"

namespace mj {

class RenderableSetup;

class Renderable {
friend class Scene;
private:
    GLuint vertexArrayObject;

public:
    Mesh *mesh;
    Shader *shader;
    GLenum drawType;
    GLenum polygonMode;
    Transform transform;

    id_t id;
    int visible;

    RenderableSetup *setupUniforms;
    RenderableSetup *setupVertexAttributes;

    Renderable(Mesh *, Shader *, GLenum drawType);
    virtual ~Renderable();
    void init();
    void render(void);
};

class RenderableSetup {
public:
    RenderableSetup() {};
    virtual void operator() (Renderable *) = 0;
};

// should be part of mesh?
// trades some space and time efficiency for extreme convenience
class VertexAttributeSetup : public RenderableSetup {
    private:
        std::vector<string> _name;
        std::vector<GLint> _components;
        std::vector<GLenum> _type;
        std::vector<GLboolean> _normalized;
        std::vector<GLsizei> _stride;
        std::vector<GLvoid *> _ptr;
    public:
    VertexAttributeSetup() {};
    // can even add enum to name to make this less stringly typed!
    void addAttribute(string name, GLint components, GLenum type, GLboolean normalized, GLsizei stride, GLvoid *ptr) {
        _name.push_back(name);
        _components.push_back(components);
        _type.push_back(type);
        _normalized.push_back(normalized);
        _stride.push_back(stride);
        _ptr.push_back(ptr);
    }
    void operator() (Renderable *r) {
        for (size_t i = 0; i < _name.size(); ++i) {
            string &name = _name[i];
            GLint &components = _components[i];
            GLenum &type = _type[i];
            GLboolean &normalized = _normalized[i];
            GLsizei &stride = _stride[i];
            GLvoid *ptr = _ptr[i];
            GLint loc = r->shader->getAttribLocation(name.c_str());
            glEnableVertexAttribArray(loc);
            glVertexAttribPointer(loc, components, type, normalized, stride, ptr);
        }
    };
};

}

#endif
